#pragma once

#include "Sock.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"
#include <iostream>
#include <unordered_map>
#include <functional>
#include <cassert>

class Connection;
class TcpServer;

const static int readyNum = 64;

typedef std::function<void(Connection *)> func_t;

class Connection // 每个套接字对应一个Connection对象
{
public:
    Connection(int sock, TcpServer *tsp)
        : _sock(sock), _tsp(tsp)
    {
    }

    void Register(func_t r, func_t s, func_t e)
    {
        _recver = r;
        _sender = s;
        _excepter = e;
    }

    void Close()
    {
        if (_sock > 0)
            close(_sock);
    }

    ~Connection()
    {
    }

public:
    int _sock;
    std::string inbuf; // 注意这里不是接收缓冲区，是这个套接字自己的缓冲区
    std::string outbuf;

    func_t _recver; // 每个套接字自己的事件处理函数
    func_t _sender;
    func_t _excepter;

    TcpServer *_tsp;
};

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t service)
        : _port(port), _service(service)
    {
    }
    ~TcpServer()
    {
        _sock.Close();
        _epoller.Close();
        if (_revs)
            delete[] _revs;
    }
    void initServer()
    {
        // 1.创建监听套接字
        _sock.Socket();
        _sock.Bind(_port);
        _sock.Listen();
        // 2.创建epoll
        _epoller.Create();
        // 3.把监听套接字托管给epoll，监听套接字只关心读即可，这里采用epoll实现多路转接，并采用ET模式
        AddConnection(_sock.FD(), EPOLLIN | EPOLLET,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);

        _revs = new struct epoll_event[readyNum]; // 给就绪返回的事件开辟空间
    }

    // 事件派发函数
    void Dispatch()
    {
        int timeout = 1000;
        while (1)
        {
            Loop(timeout);
        }
    }

private:
    void Recver(Connection *conn)
    {
        char buf[1024];
        while (1)
        {
            int n = recv(conn->_sock, buf, sizeof(buf) - 1, 0);
            if (n > 0)
            {
                buf[n] = 0;
                conn->inbuf += buf; // 将数据读到sock的接收缓冲区
                _service(conn);
            }
            else if (n == 0) // 客户端关闭
            {
                if (conn->_excepter)
                {
                    conn->_excepter(conn);
                    return;
                }
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) // 非阻塞  已经读完，没有数据可读
                    break;
                else if (errno = EINTR) // 被打断了
                    continue;
                else
                {
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn);
                        return;
                    }
                }
            }
        }
    }
    void Sender(Connection *conn)
    {
        while (1)
        {
            int n = send(conn->_sock, conn->outbuf.c_str(), conn->outbuf.size(), 0);
            if (n >= 0)
            {
                if (n < conn->outbuf.size()) // 没发完，把已经发的擦除掉
                    conn->outbuf.erase(0, n);
                else                        // 发完了
                {
                    conn->outbuf.erase(0, n);
                    break;
                }
                // if (conn->outbuf.empty())   //发完了
                //     break;
                // else
                //     conn->outbuf.erase(0, n);
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) // 非阻塞  发送条件不满足，发送缓冲区满了
                    break;
                else if (errno == EINTR) // 发送被中断，继续while
                    continue;
                else // 发送出错
                {
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn);
                        return;
                    }
                }
            }
        }
        // 到此，要么发完了，要么没发完
        if (!conn->outbuf.empty()) // 如果outbuf里不为空，说明没发完，要设置关心该sock的写事件，满足发送条件了再发
            conn->_tsp->EnableReadWrite(conn, true, true);
        else
            conn->_tsp->EnableReadWrite(conn, true, false); // 为空，则取消对该套接字的写事件的关心，避免浪费
                                                            // 因为只要对接收缓冲区没满，我们的写事件就一直就绪，而我们
                                                            // 不是经常要写，但它会一直通知，就会占用cpu资源
    }
    void Excepter(Connection *conn)
    {
        _epoller.Control(conn->_sock, 0, EPOLL_CTL_DEL);
        conn->Close();
        _conns.erase(conn->_sock);
        logMessage(DEBUG, "套接字%d被关闭", conn->_sock);
        delete conn;
    }

    //_listenSock的处理就绪事件的函数
    void Accepter(Connection *conn)
    {
        while (1)
        {
            std::string clientIp;
            uint16_t clientPort;
            int err = 0;
            int sock = _sock.Accept(&clientIp, &clientPort, &err);
            if (sock > 0)
            { // 每创建一个sock都要对应的维护一个Connection，并把sock托管
                AddConnection(sock, EPOLLIN | EPOLLET,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
                logMessage(DEBUG, "连接成功，[%s:%d]", clientIp.c_str(), clientPort);
            }
            else
            {
                // 由于设置了非阻塞，所以没有链接和连接失败都反返回-1，通过errno来区分
                if (err == EAGAIN || err == EWOULDBLOCK) // 表明所有连接已读完
                    break;
                else if (err = EINTR) // 被打断了
                    continue;
                else
                {
                    logMessage(ERROR, "连接错误，%d: %s", err, strerror(errno));
                    return;
                }
            }
        }
    }

    // 给每个套接字都维护一个Connection，并托管给epoll
    // sock：套接字  events：关心的事件  r、s、e：该套接字对应事件的处理函数
    void AddConnection(int sock, uint32_t events, func_t recver, func_t sender, func_t excepter)
    {
        // 如果有EPOLLET，才设为非阻塞
        if (events & EPOLLET)
            Util::setNonBlock(sock);
        // 给sock创建一个Connection，初始化并添加到_conns
        Connection *conn = new Connection(sock, this);
        conn->Register(recver, sender, excepter); // 设置该sock对应的处理事件的函数

        // 将sock与要监视的事件托管给epoll
        bool ret = _epoller.AddEvent(sock, events);
        assert(ret);
        (void)ret;

        _conns.insert(std::make_pair(sock, conn));

        logMessage(DEBUG, "套接字%d托管成功", sock);
    }

    void Loop(int timeout)
    {
        int n = _epoller.Wait(_revs, readyNum, timeout);
        for (int i = 0; i < n; i++)
        {
            int sock = _revs[i].data.fd; // 取出就绪的fd和事件
            uint32_t events = _revs[i].events;

            // 把异常事件也归类到读写事件
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);

            // a.读事件 b.该文件描述符存在对应的Connection c.该sock的读事件的处理函数不为空
            // 无需区分是listenSock还是普通的sock，因为每个sock对应的Connection中都注册了各自的事件处理函数
            if (events & EPOLLIN && IsConnectionExist(sock) && _conns[sock]->_recver)
                _conns[sock]->_recver(_conns[sock]);

            if (events & EPOLLOUT && IsConnectionExist(sock) && _conns[sock]->_sender)
                std::cout << "写" << std::endl;
        }
    }

    // 判断该sock是否有对应的Connection在map中
    bool IsConnectionExist(int sock)
    {
        auto iter = _conns.find(sock);
        return iter != _conns.end();
    }

    bool EnableReadWrite(Connection *conn, bool readable, bool writeable)
    {
        uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
        _epoller.Control(conn->_sock, events, EPOLL_CTL_MOD);
    }

private:
    uint16_t _port;
    Sock _sock;
    Epoller _epoller;
    std::unordered_map<int, Connection *> _conns; // 保存sock 和Connection的键值对
    struct epoll_event *_revs;                    // 存储就绪事件
    func_t _service;
};